package com.h2.gwt.util.net;

import java.util.AbstractList;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.h2.gwt.util.com.google.gwt.core.client.JsMap;
import com.h2.gwt.util.lang.ICallBack;

public final class HttpUtils {
   
   private HttpUtils() {
      //utility constructor
   }
   
   public static enum Protocol {
      HTTP("http"), HTTPS("https"), FTP("ftp"), HTTP_TLS("https-tls");
      
      private String _name;
      
      private Protocol(String name) {
         _name = name;
      }
      
      @Override
      public String toString() {
         return _name;
      }
   }

   public static void request(final ICallBack<String> callback, String url) {
      
      RequestBuilder builder = null;
      builder = new RequestBuilder(RequestBuilder.GET,
               url);
      try {
         builder.sendRequest(null, new RequestCallback() {

            public void onError(Request request, Throwable exp) {
               // TODO
               exp.printStackTrace();
            }

            public void onResponseReceived(Request request, Response response) {
               callback.callBack(response.getText());
            }
         });
      } catch (RequestException exp) {
         // TODO
      }
   }
   
   public static <E extends JavaScriptObject> List<List<E>> parseJsonListList(
         String jsonStr) {
      final JsArray<JsArray<JavaScriptObject>> arr = parseJsonArrArr(jsonStr);
      
      return new AbstractList<List<E>>() {

         @Override
         public List<E> get(int index) {
            final JsArray<JavaScriptObject> arr2 = arr.get(index);
            if(arr2 == null) {
               return Collections.emptyList();
            }
            return new AbstractList<E>() {

               @SuppressWarnings("unchecked")
               @Override
               public E get(int index) {
                  return (E) arr2.get(index);
               }

               @Override
               public int size() {
                  return arr2.length();
               }
            };
         }

         @Override
         public int size() {
            return arr.length();
         }
      };
   }
   
   public static <K, E extends JavaScriptObject> Map<K, E> parseJsonMap(
         String jsonStr) {
      final JsMap<K, JavaScriptObject> map = evalJsonMap(jsonStr);
      
      return new AbstractMap<K, E>() {

         @Override
         public Set<Entry<K, E>> entrySet() {
            Set<Entry<K, E>> results = new LinkedHashSet<Entry<K, E>>();
            Set<K> keys = map.keySet();
            for (final K key : keys) {
               results.add(new Entry<K, E>() {

                  @Override
                  public K getKey() {
                     return key;
                  }

                  @SuppressWarnings("unchecked")
                  @Override
                  public E getValue() {
                     return (E) map.get(key);
                  }

                  @Override
                  public E setValue(E value) {
                     return null;
                  }
               });
            }
            return results;
         }
      };
   }
   
   public static <K, E extends JavaScriptObject> Map<K, List<E>> parseJsonMapList(
         String jsonStr) {
      final JsMap<K, JsArray<JavaScriptObject>> map = parseJsonMapArr(jsonStr);
      
      return new AbstractMap<K, List<E>>() {

         @Override
         public Set<Entry<K, List<E>>> entrySet() {
            Set<Entry<K, List<E>>> results = new HashSet<Entry<K, List<E>>>();
            Set<K> keys = map.keySet();
            for (final K key : keys) {
               results.add(new Entry<K, List<E>>() {

                  @Override
                  public K getKey() {
                     return key;
                  }

                  @Override
                  public List<E> getValue() {
                     final JsArray<JavaScriptObject> arr = map.get(key);
                     if(arr == null) {
                        return Collections.emptyList();
                     }
                     return new AbstractList<E>() {

                        @SuppressWarnings("unchecked")
                        @Override
                        public E get(int index) {
                           return (E) arr.get(index);
                        }

                        @Override
                        public int size() {
                           return arr.length();
                        }
                     };
                  }

                  @Override
                  public List<E> setValue(List<E> value) {
                     return null;
                  }
               });
            }
            return results;
         }
      };
   }
   
   public static List<String> parseJsonListOfString(
         String jsonStr) {
      final JsArrayString arr = parseJsonArrOfString(jsonStr);
      
      if(arr == null) {
         return Collections.emptyList();
      }
      return new AbstractList<String>() {
         @Override
         public String get(int index) {
            return (String) arr.get(index);
         }

         @Override
         public int size() {
            return arr.length();
         }
      };
   }
   
   public static <E extends JavaScriptObject> List<E> parseJsonList(String jsonStr) {
      final JsArray<JavaScriptObject> arr = parseJsonArr(jsonStr);
      
      if(arr == null) {
         return Collections.emptyList();
      }
      return new AbstractList<E>() {

         @SuppressWarnings("unchecked")
         @Override
         public E get(int index) {
            return (E) arr.get(index);
         }

         @Override
         public int size() {
            return arr.length();
         }
      };
   }
   
   public static native <K> JsMap<K, JavaScriptObject> evalJsonMap(String jsonStr) /*-{
      return eval('(' + jsonStr + ')');
   }-*/;
   
   public static native <K> JsMap<K, JsArray<JavaScriptObject>> parseJsonMapArr(String jsonStr) /*-{
      return eval('(' + jsonStr + ')');
   }-*/;
   
   public static native JsArray<JsArray<JavaScriptObject>> parseJsonArrArr(String jsonStr) /*-{
      return eval('(' + jsonStr + ')');
   }-*/;
   
   public static native JsArray<JavaScriptObject> parseJsonArr(String jsonStr) /*-{
      return eval('(' + jsonStr + ')');
   }-*/;
   
   public static native JsArrayString parseJsonArrOfString(String jsonStr) /*-{
      return eval('(' + jsonStr + ')');
   }-*/;
   
   public static native JavaScriptObject parseJson(String jsonStr) /*-{
      return eval('(' + jsonStr + ')');
   }-*/;
}
